package com.gallop.common;

import com.gallop.common.cache.CacheExecutor;
import com.gallop.common.cache.CacheQueryConstants;
import com.gallop.common.cache.assemble.OrderedJoiningKeyAssembleHandler;
import com.gallop.common.cache.config.CacheConfigProperties;
import com.gallop.common.cache.execute.InMemoryCacheExecutor;
import com.gallop.common.cache.execute.NopCacheExecutor;
import com.gallop.common.cache.execute.RedissonCacheExecutor;
import com.gallop.common.lock.DistributeLocker;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.redisson.spring.starter.RedissonAutoConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;

@Slf4j
@Configuration
@EnableConfigurationProperties(CacheConfigProperties.class)
@AutoConfigureAfter(RedissonAutoConfiguration.class)
@ComponentScans({
        @ComponentScan("com.gallop.common"),
})
public class CacheAutoConfiguration {

    @Autowired
    private CacheConfigProperties cacheConfigProperties;
    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 按照配置的策略，创建默认缓存执行器，未配置时使用{@link NopCacheExecutor}
     */
    @Bean
    @Primary
    public CacheExecutor getCacheExecutor() throws Exception {
        CacheConfigProperties.Strategy cfgStrategy = cacheConfigProperties.getExecutorStrategy();
        if (cfgStrategy == CacheConfigProperties.Strategy.MEMORY) {
            return inMemoryCacheExecutor();
        }
        if (cfgStrategy == CacheConfigProperties.Strategy.REDIS) {
            return redissonCacheExecutor();
        }
        return nopCacheExecutor();
//        CacheQueryConstants.ExecutorStrategy executorStrategy = CacheQueryConstants.ExecutorStrategy.valueOf(cfgStrategy.name());
//        Class<? extends CacheExecutor> clazz = executorStrategy.getExecutorClazz();
//        return clazz.newInstance();

    }

    /**
     * 基于Redisson的redis缓存策略执行器，依赖{@link org.redisson.api.RedissonClient}，需要有redis相关配置
     */
    @Bean
    public RedissonCacheExecutor redissonCacheExecutor() throws Exception {
        Class<? extends CacheExecutor> clazz = CacheQueryConstants.ExecutorStrategy.REDIS.getExecutorClazz();
        return (RedissonCacheExecutor) clazz.newInstance();
    }

    /**
     * 基于内存的缓存策略执行器
     */
    @Bean
    public InMemoryCacheExecutor inMemoryCacheExecutor() throws Exception {
        Class<? extends CacheExecutor> clazz = CacheQueryConstants.ExecutorStrategy.MEMORY.getExecutorClazz();
        return (InMemoryCacheExecutor) clazz.newInstance();
    }

    /**
     * 缺省无操作的缓存执行器
     */
    @Bean
    public NopCacheExecutor nopCacheExecutor() throws Exception {
        Class<? extends CacheExecutor> clazz = CacheQueryConstants.ExecutorStrategy.NOP.getExecutorClazz();
        return (NopCacheExecutor) clazz.newInstance();
    }

    /**
     * 缓存key参数拼接器
     */
    @Bean
    @ConditionalOnMissingBean(CacheKeyAssembleHandler.class)
    public CacheKeyAssembleHandler getAssembleHandler() {
        return new OrderedJoiningKeyAssembleHandler();
    }

//    @Bean
//    @ConditionalOnMissingBean(RedissonClient.class)
//    public RedissonClient defaultRedissonClientProxy() {
//        log.warn("RedissonClient not found, using proxy instead. Forgot \"spring.redis.*\" in yml/properties configuration?");
//        Enhancer enhancer = new Enhancer();
//        enhancer.setSuperclass(RedissonClient.class);
//        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
//            if ("shutdown".equals(method.getName())) {
//                return null;
//            }
//            throw new IllegalAccessException("RedissonClient not found!  Forgot \"spring.redis.*\" in yml/properties configuration?");
//        });
//        return (RedissonClient) enhancer.create();
//    }

    /**
     * 分布式锁单例bean
     */
    @Bean
    public DistributeLocker distributeLocker() {
        return DistributeLocker.init(applicationContext.getBean(RedissonClient.class),
                getAssembleHandler());
    }
}
